Hey there.
Welcome back.
In this lecture we're going to be discussing a new type of script called a module script.
A module script is a type of Lua source container that runs once and must return exactly one value.
This value is then returned by a call to the require function.
Given the module script as the only argument.
Module scripts run once and only once per Lua environment and return the exact same value for subsequent
calls to require.
Module scripts are essential objects for adhering to the don't repeat yourself principle.
When you write a function, write it only once and use it everywhere.
Having multiple copies of a function is disastrous when you need to change that behavior.
So you should define functions or groups of functions in module scripts and have your scripts and local
scripts call require on your module scripts.
Keep your code organized so as roadblocks themselves.
State module scripts are important tools for making your code more organized, sustainable, and it
helps you to avoid copy and pasting code all over the place.
As the name suggests, you can basically think of a module script as some kind of shareable module that
you can, like, plug into other scripts to provide more features.
Now, one statement you might be confused on is the statement that module scripts run once and only
once per Lua environment.
What is a Lua environment?
Well, that would basically be a fancy name for the scope or the context the Lua interpreter creates
when executing code in your game.
There are multiple environments in Roblox.
For example, every single script in your game has its own environment for local variables.
You can't share those local variables across different scripts.
Other scripts can't access it because they're not in the same environment.
But all scripts in the game also share a global environment for sharing global variables.
You can basically think of it as two different biomes on Earth, like a desert and a forest.
Each of them are their own environment, yet both of them share the same global environment, which
is the planet Earth.
Now, the only exception to this run once and only once.
Fact is, if you have code executing and what we call parallel, which means that multiple processor
threads are executing code at the exact same time.
This is a much more advanced topic we don't discuss until much later in the course.
So your main takeaway here should be that module scripts run once when they are first required, and
then they'll return the same value for any future calls to the require function.
They don't run the code inside of them again every single time you call the require function.
So inside of studio let's go ahead and create a brand new module script.
I'll just place mine in server Script service so we can search for a module script.
Here it is.
There's our beautiful module script.
And inside of our module script you're going to notice some default code inside of here.
And as you can see it's returning a table.
And as we read in the documentation a module script has to return exactly one value.
Now that one value could actually be anything.
Most commonly it's going to be a table, but other times you may have module scripts that return functions,
or maybe other data types.
You aren't limited to what exactly you can return from a module script.
It just has to be one value that you return.
Now, the first claim I want to test in the documentation is that module scripts only run once, even
if you call the require function on it multiple different times.
So what we're going to do is we're just going to print inside of the module script, the module executed.
And then let's go ahead and run our game.
And what you're going to notice is that nothing prints inside of the console.
Why is that.
Well silly me, we need to require the module script because right now it's just acting as a container
of code, and it's not doing anything with it for this code inside of the module to actually execute.
We need to have either a local script or a server script require the module.
So let's add in a server script into the server script service.
And let's go ahead and require our module script.
So we could call this module.
And that's going to be equal to.
And the fastest way I can reference this module script is I can just do script dot parent dot module
script.
And then we need to use that function they talked about in the documentation called require.
And this is a global function.
And we need to pass it the module script itself.
And it's going to return us one value which is the value stored inside of the module script.
In this case that's our empty table.
So let's go ahead and call require and let's pass it our module script.
So now what's stored in this variable is going to be our empty table.
And to confirm that let's just go ahead and print this variable into the console.
Now if we go ahead and run our game what you're going to see is that the module executed.
And as you can see there is our empty table stored inside of our variable.
Very cool.
Now let's do something a little bit fun.
Let's try requiring the module script again.
So we'll do module is equal to require.
And let's just do the exact same thing.
And let's see if this print statement right here executes more than once.
So if we run the game well why won't you take a look at that.
It only printed once and we still have our empty table.
So their claims are true, even though we called the require function twice on our module script.
This print statement right here only executed once.
Now let me show you a useful feature of module scripts.
And one of those useful features is the ability to share functions across multiple different scripts.
So we can go ahead and actually store a function within the table that gets returned from our module.
And the syntax to do that is we're first going to use the function keyword.
Then we need to reference the table that we're going to store the function in which is just called module.
And then we need to put a dot.
And what comes after the dot is going to be the name of our function.
I'm just going to call this example function.
And we'll put our two parentheses and then hit enter to create our end keyword.
So this is our new function scope right here.
And now we have created a function that exists within our module.
So if we actually go back to our script here let me delete this redundant line.
And then let me go ahead and just index my module.
What you're going to see is my example function appears right here.
It tells us it's a function that takes no parameters and returns nothing.
And I'm able to call this function within my script.
And the cool thing about this is that I can actually call this function as many times as I'd like.
And inside of this function, let's just go ahead and print the function executed.
So if we go ahead and run the game now, what you're going to see is that when we first required our
module, it prints the module executed.
But every single time we call the function, it printed four different times because we called it four
different times.
Another great feature of module scripts is the ability to share data across multiple scripts.
So when one script, for example, let's say this script makes a change to the module, any other scripts
that have required the module are also going to observe that change.
So let's say inside of my module I had a variable like let's say there's a variable in this table.
Let's make one real quick.
We can do module dot.
Uh let's call the variable hello.
And let's set it equal to a value of ten.
Whatever.
Just for this example inside of this script, let's just go ahead and print out what module Dot hello
is, which is going to print ten.
And then let's wait um, let's say one second and then let's print it again.
And this time, what I'm going to do is I'm going to create another script inside of server script service.
This one's also going to require my module.
So let me just copy the code in here and put it in my new script in my new script.
And this time what I want this module to do is I'm going to have it wait for, let's say 0.1 seconds
and then we're going to print, and then we're going to set module dot hello equal to 20 instead.
So that way the timing is correct because I don't know which one of these scripts is going to execute
first.
This one might execute first or this one might execute first.
If I didn't have this yield statement here.
And let's say this script executed first, well it's going to immediately update the value inside of
my module.
And then when this script runs it's already going to observe that change.
So it'll print 20 here and then it'll print 20 here.
Again because this script already made the change to that variable.
So we're just going to wait for 0.1 seconds before we make the change.
That way this script has the opportunity to print the original value out which is ten.
So if we run our script module executed, it prints ten, and then it waits one second and then it prints
20.
So as you can see, even though these are two different scripts, this script was able to observe the
change made to the hello variable, even though this other script made that change.
And that's because we can share data across scripts using module scripts.
Another important thing to note about observing changes made to values inside of module scripts is that
those changes can only be observed if the scripts are of the same type, and the scripts exist on the
same computer.
So, for example, if a server script were to make a change to my module script, and then let's say
a local script also required that module script, that local script is not going to be able to observe
the changes made by the server script.
And if the local script makes any changes, the server will not be able to observe any of those changes.
To prove this, let's go ahead and migrate our module script into replicated storage.
Let me go ahead and get rid of this script because I do not need it right now.
And now let's go ahead and update our reference to our module script.
So I'm just going to do game dot replicated storage and get my module script so I can require it.
And then what I'm going to do is I'm going to just wait, uh, let's say six seconds and I'm just going
to print what the value of hello is going to be.
And then what I'm going to do next is I'm going to create a new script inside of starter player scripts.
This is going to be my local script.
And this local script is also going to require my module.
So I'm going to do game dot replicated storage dot module script.
And let's go ahead and update the value of hello to uh 20.
Now let me go ahead and play test the game.
So we'll do play.
So that way my client comes in.
If you take a look at that the print statement executed twice once by the server, once by the client,
and then it printed ten on the server, even though the client updated the value inside of the module
to 20.
That's because, as I said, the changes to the module script are not observed across the client server
boundary.
You'll also notice that our module script executed twice, like I said, once on the server and once
on the client.
And that's because this module script exists on two separate environments, or basically two different
environments are requiring the module script, which is why it executed twice.
One of the environments was on the server and the other environment was on this specific client.
Now, as I mentioned earlier, module scripts are not limited to only returning tables.
We can also return any other data type such as functions.
So let's say we wanted to create some kind of general use function that any script in the game could
use, because it's a pure function.
And a pure function is simply the name given to a function that returns the exact same output when it's
given the same input, and it does not rely on any external data.
It's a fully encapsulated function, and encapsulated is another big word, but it basically means that
the function is enclosed, like if you were to enclose something in a capsule and it does not interact
with anything outside of the capsule.
So instead what we're going to do here is we're going to have our module script return a function.
So what we can go ahead and do is we can create a new function inside of here.
I'm just going to call this example function.
And instead of returning that table we're going to return example function.
But we don't want to call example function because that's going to return the return value of this function.
Instead we want to return a reference to this function.
So that way other scripts can execute this function and use it.
And inside of this function we can just print something like the function executed.
We'll put a little smiley face.
So now when I require my module script this variable is no longer storing a table, but instead it's
storing a function.
And that means I can call it like this.
And then this local script is going to error because this module is no longer a table.
So let me just get rid of this local script for now.
And then let's go ahead and run the game and look at that.
The function executed smiley face.
Because my variable here is now storing a reference to this function that we returned from the module
script.
So this just proves that module scripts are not limited to tables.
You can return other data types.
Now we've just scratched the surface with the possibilities of module scripts, and you can do so much
more with them.
It just depends on the context of what you need and how you want to utilize modules.
You could, for example, use a module script to create your own custom enum, and we'll be talking
about enums in a future lecture.
You could use a module script to act as a class.
When you use object oriented programming, you could use a module script as a place to store settings
in your game and share those settings across multiple scripts and so much more uses for module scripts.
So let's go ahead and do a little bit of some practice with module scripts.
So what I would like for you to do is I would like for you to create a new module script.
And this module script is going to act as a calculator.
So you could just call it like calculator module or something like that.
And I want you to add four functions inside of it.
One that adds, one that subtracts, one that multiplies, and one that divides.
And for the division function, I want you to make sure that the user is not able to divide by zero.
So all those functions should take two numbers as arguments.
You could call it a and B, and each of those functions should do their own specific role.
So one is going to add one is going to subtract.
And this module should be usable across multiple different scripts.
So please take a few minutes to work on this practice activity.
And I will go ahead and show you my solution next okay.
So let's go ahead and create a new module script.
I'm just going to place mine in replicated storage.
And I'm going to name this module my calculator.
And then inside my module script I could just keep the variable name as module.
But just to be consistent I'll call it calculator.
And I need four functions inside of my calculator.
So one of the functions is going to be an addition function that takes two numbers.
I'll just call it a and b.
And then we need three more functions one for subtraction, one for multiplication and one for division.
So let me rename each of these functions.
This one's going to be subtract.
This one is going to be multiply and this one's going to be divide.
And then we can just simply return the result of each of these operations.
So for the addition we can just return a plus B subtraction.
We can just return a minus b multiply.
We can return a times b.
And then for division we need to make sure that before we return A divided by B, we need to make sure
that B is not zero.
So let's go ahead and make a check real quick.
And if B is equal to zero, which we don't want, then we can just return something.
I mean you could return zero or maybe you could return a string like can't divide by zero, maybe something
like that.
Otherwise, if B is not equal to zero, we will return this operation here.
So now if we go back to our server script and we go ahead and require this new calculator module script.
So calculator I'll just call this variable my calculator.
We should be able to use all of its functions like dividing adding multiplying and subtracting.
So let's go ahead and try it out.
Let's print the result of some division here.
Let's just do five and ten.
And then actually let's just copy this and do it for each function.
So we'll do addition.
We'll do subtraction.
We'll do multiplication and division.
And then if we go ahead and run the game, we get 15 -550 and 0.5.
Awesome.
And then if I try to divide by zero, hopefully we should get that string printed in the console.
There you go.
We cannot divide by zero.
Now this is obviously not a very useful module.
I mean, we could just simply use the mathematical operators without needing to create functions for
them.
But I just wanted this to serve as a practice problem with modules so you feel more comfortable using
them and creating functions inside of them.
So that's going to be all from me for this lecture.
If you have any questions relating to this lecture, please make sure to join the discord so you can
get quick support from me and other students and otherwise.
Thank you for watching and I'll see you in the next lecture.